<?php declare(strict_types=1);


namespace App\Http\Middleware;


use App\Helper\RedisConst;
use App\Model\Logic\UUIDCheckLogic;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Annotation\Mapping\Inject;
use Swoft\Console\Helper\Show;
use Swoft\Context\Context;
use Swoft\Http\Message\ContentType;
use Swoft\Http\Server\Contract\MiddlewareInterface;

/**
 * @Bean()
 * Class AuthMiddleware
 * @package App\Http\Middleware
 */
class AuthMiddleware implements MiddlewareInterface
{

    /**
     * @Inject()
     * @var UUIDCheckLogic
     */
    private $uuidCheck;
    /**
     * @Inject("redis.pool")
     * @var
     * 默认连接池
     */
    private $redis;


    /**
     * @Inject("redisLog.pool")
     * @var
     */
    private $redisLog;

    /**
     * @Inject("redis2.pool")
     * @var
     */
    private $redis2;

    /**
     * 令牌认证
     * @param ServerRequestInterface $request
     * @param RequestHandlerInterface $handler
     * @return ResponseInterface
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        //通用令牌
        $token = $request->getHeaderLine('token');
        $uri = $request->getServerParams()['request_uri'];

        if($token === 'FkUw1pOFkUw1pOBHh7xSI8jWf0X6JuryjWHjhuMapX69FKZSVgBHh7xSI8jWf0X6JuryjWHjhuMapX69FKZSVg') return $handler->handle($request);
        //部分开放接口
        if(in_array($uri, ['/member/logout','/system/menu/sublist','/ios','/app/shortcut','/member/review_permission','/yunxin/token','/use/invite/code','/sign/timezone/add','/fcmtoken', '/brands', '/posts/lists', '/system/version','/system/nation', '/system/menulist','/ads/muti', '/post/label/columns', '/ads', '/system/preconfig','/report/reason'])) return $handler->handle($request);
        if(in_array($uri, ['/sign/in', '/passport/login'])) return $handler->handle($request);
        //设置客户端时间差值
        if(in_array( $uri, ['/app/onlinetime/cnt','/ads', '/yunxin/token','/system/version', '/ads/datacnt'])) $this->clientTimeStamp($request->serial_number, $request->timestamp);


        //检查用户的IP 和 用户的设备号
        $ip = $request->getHeaderLine('x-forwarded-for');
        if($this->checkIpAndDevice($ip, $request->serial_number)) return $this->response(10132);

        //未登录


        //登录用户令牌
        $headers = $this->getEncryptHeaders($request);

        //设备登录用户数检测
        if(!empty($headers['uuid'])){
//            //用户参数验证
            $login = $this->uuidCheck->getMemberId($headers['uuid']);
            if(!$login) return $this->response(10132);
            $ck = $this->DeviceDailyLoginUsers($request->serial_number, $uri, $login);
            if($ck) return $this->response(10132);
        }


        //token时效性验证 10秒内有效
        $client_time = $this->getClientTimeStamp($request->serial_number);
        $substr = substr($request->timestamp,0, 10);
        $abs = abs(time() - intval($substr)) - abs($client_time);
        $error_lang = !in_array($request->lang, ['en', 'ind', 'ru'])  ? 'en' : $request->lang;
        if(  $abs > 20 ) {
            $log = JSON([
                'request_ip' => $ip,
                'env' => env('APP_ENV'),
                'url' => $uri,
                'error_type' => 'timestamp_error',
                'server_time' => time(),
                'headers'  =>  $headers,
            ]);
            $this->redisLog->rpush('vaffle-business', $log);
            return $this->response(10150 , "");
        }


        $body = $this->getBody($request);
        $arr = array_merge($headers, $body);
        ksort($arr);
        $str = '';
        foreach($arr as $key => $val)
        {
            $str .= $key.'='.$val.'&';
        }

        $app_key = env('APP_KEY');
        if(!in_array($headers['device'], ['android','ios','web'])) return $this->response();
        if($headers['device'] === 'android') $app_key = '719fa7c4c88aa0a682F30BF346A31Ff2c2f9cbcfdf286121f220cb';
        if($headers['device'] === 'ios') $app_key = '7c4c88aa0a68FkUw1pOBHh7xSI8jWf0X6JuryjWHjhuMapX69FKZSVgf2c2f9c';
        if($headers['device'] === 'android' && $headers['version'] >= '3.6.6') $app_key = '7xSI8jWf0719fa7c4c88aa0a682F30BF346A31Ff2c2f9cbcfdf286121f220cb';
        if($headers['device'] === 'ios' && $headers['version'] >= '3.6.6') $app_key = '7xSI8jWf07c4c88aa0a68FkUw1pOBHh7xSI8jWf0X6JuryjWHjhuMapX69FKZSVgf2c2f9c';

        if(isset($headers['uuid']) && $headers['uuid']) {
            $mix_str_key = mixSecretKey($headers['serial_number'], $headers['uuid']);
            $app_key = $this->redis->get($mix_str_key);
        }

        $encrypt_str = $str.$app_key;
        //app token令牌验证
        if($token === strtoupper(md5($encrypt_str))) return $handler->handle($request);

        $response = $handler->handle($request);
        return $response;
    }

    /**
     * 获取需要加密的头部参数
     * @param $request
     * @return array
     */
    protected function getEncryptHeaders($request)
    {
        $headers = [
            'serial_number' => $request->serial_number,
            'version' => $request->version,
            'phone_model' => $request->phone_model,
            'device' => strtolower($request->device),
            'system_version' => $request->system_version,
            'company' => $request->company,
            'timestamp' => $request->timestamp,
            'lang' => $request->lang,
        ];
        if(isset($request->getHeader('uuid')[0])) $headers['uuid'] = $request->getHeaderLine('uuid');
        if(isset($request->getHeader('login')[0])) $headers['login'] = $request->getHeaderLine('login');
        return $headers;
    }

    /**
     * 获取body参数
     * @param $request
     * @return mixed
     */
    protected function getBody($request)
    {
        return $request->getParsedBody();
    }


    /**
     * 记录设备当前时间 与服务端的时间戳
     * @param $serial_number
     * @param $client_time
     */
    public function clientTimeStamp($serial_number, $client_time)
    {
        if(!$client_time) {
            $client_time = time();
        }
        $key = 'clientTimeDiff:'.sha1($serial_number);
        $diff =  time() - substr($client_time,0, 10);
        $this->redis->set($key, $diff);
    }

    /**
     * 获取设备当前时间 与服务端的时间戳
     * @param $serial_number
     * @return int
     */
    public function getClientTimeStamp($serial_number)
    {
        $key = 'clientTimeDiff:'.sha1($serial_number);
        return (int) $this->redis->get($key);
    }


    //错误返回
    public function response($code = 500, $message = "")
    {
        return Context::mustGet()->getResponse()
            ->withContentType(ContentType::JSON)
            ->withData([
                'code' => $code,
                'msg'  => $message,
                'data' => new \stdClass(),
            ]);
    }


    /**
     * 设备登录用户数检测
     * @param $serial_number
     * @param $uri
     * @param $member_id
     * @param $lang
     * @return \Swoft\Http\Message\Response|\Swoft\WebSocket\Server\Message\Response
     */
    public function DeviceDailyLoginUsers($serial_number, $uri, $member_id)
    {
        if(!in_array($uri, ['/member/logout']) && $member_id){
            $ch_code = $this->userDeviceDailyBind($serial_number, $member_id);
            if(is_numeric($ch_code)) {
//                if(!isset(config('lang_'.$lang)[10149])) $lang = 'en';
                return 10149;
            }
        }
        return false;
    }


    public function userDeviceDailyBind($serial_number, $login)
    {
        if(!is_numeric($login)) return;
        $key = 'userLoginDevice:'.date('Ymd').sha1($serial_number);
        $logins = $this->redis->sMembers($key);
        if(in_array($login, $logins)) return;
        if(!in_array($login, $logins) && count($logins) < config('app.device_login_user_limit', 5)){
            $this->redis->sadd($key, $login, RedisConst::NORMAL_EXPIRE);
        } else {
            return 10149;
        }
    }


    /**
     * 检查用户的IP 和 用户的设备号
     * @param $client_ip
     * @param $serial_number
     * @return bool
     */
    public function checkIpAndDevice($client_ip, $serial_number)
    {
        return $this->_checkIp($client_ip) || $this->_checkDevice($serial_number);
    }


    //检查IP
    private function _checkIp($ip)
    {
        $sensitive_words = $this->redis2->get(RedisConst::PROJECTCONFIG.'app_sensitive_ip');
        $block_ips = explode(',', $sensitive_words);
        $block_ips = trimItem($block_ips);
        if(!$block_ips) return false;
        return in_array($ip, $block_ips);
    }

    //检查设备
    private  function _checkDevice($device_number)
    {
        $sensitive_words = $this->redis2->get(RedisConst::PROJECTCONFIG.'app_sensitive_serial_number');
        $device_numbers = explode(',', $sensitive_words);
        $device_numbers = trimItem($device_numbers);
        if(!$device_numbers) return false;
        return in_array($device_number, $device_numbers);
    }

    //用户参数验证

    //采用appkey 和 appsecret




}